package test;

import cn.hutool.core.io.FileUtil;
import jdk.internal.org.objectweb.asm.*;
import org.springframework.util.StringUtils;
import test.pojo.User;


import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static jdk.internal.org.objectweb.asm.Opcodes.ASM5;

/**
 * Created with IntelliJ IDEA.
 *
 * @ project name : example
 * @ Author: XuLeHuang
 * @ Date: 2022/11/29 22:42
 * @ Description:
 */

public class ASM {
    /**
     * 分析：
     * 这么做的原因：
     * 每个线程无论是使用ThreadLocal还是局部方法创建map，都能保证线程安全，
     * 但是ThreadLocal相较局部不容易释放空间，因为它是GCROOT，局部的就不一样，方法生命周期（pop）一般就有没有GCROOT，被回收的几率大，
     * 不过，有一点值得提的是，如果因为死循环不断getset，就不断创建map或者因为业务需要大量getset导致短时间内生成大量map（特别注意多线程并发）
     * 就而来不及回收可能导致内存溢出
     * 因此，两者的使用，应该根据系统业务等综合考虑*/
    private static final ThreadLocal<Map<String , String>> paramMapThreadLocal = new ThreadLocal<Map<String , String>>() {
        @Override
        protected Map<String , String> initialValue() {
            Map<String , String> paramMap = new HashMap<String , String>();
            return paramMap;
        }
    };
    public static void main(String[] args) throws IOException {
        ASM asm = new ASM();
        Package aPackage = User.class.getPackage();
        System.out.println(aPackage.getName());
        System.out.println(User.class.getName());
        String sourceClassFileFullName = User.class.getName().replace("." , "/");
        asm.asmTest(sourceClassFileFullName);
        User user = new User();
        System.out.println(user);
        String newWord = firstCharToUpCaseForWord("hh");
        System.out.println(newWord);
    }
    public  void asmTest(String sourceClassFileFullName) throws IOException {
        /*存参数名-》类型*/
        Map<String , String> paramMap = paramMapThreadLocal.get();
        //Map<String , String> paramMap = new HashMap<String , String>();
        ClassReader cr = new ClassReader(sourceClassFileFullName);
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
        ClassVisitor cv = new ClassVisitor(ASM5, cw ) {
            @Override
            public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
                paramMap.put(name , desc);
                return super.visitField(access, name, desc, signature, value);
            }

            @Override
            public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
                if (name.startsWith("get") || name.startsWith("set")) {
                    return null;
                }
                return super.visitMethod(access, name, desc, signature, exceptions);
            }

            @Override
            public void visitEnd() {
                Set<Map.Entry<String, String>> nameAndTypeSet = paramMap.entrySet();
                nameAndTypeSet.forEach(nameAndTypeItem -> {
                    /*get方法*/
                    /*TODO 将方法首字母大写，方法返回值，无参*/
                    MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC , "get" + firstCharToUpCaseForWord(nameAndTypeItem.getKey()) , "()" + nameAndTypeItem.getValue() ,
                            null , null);

                    mv.visitCode();
                    /*TODO 方法体*/
                    mv.visitVarInsn(Opcodes.ALOAD, 0);
                    mv.visitFieldInsn(Opcodes.GETFIELD, sourceClassFileFullName, nameAndTypeItem.getKey(), nameAndTypeItem.getValue());
                    mv.visitMaxs(0 , 0);
                    mv.visitInsn(Opcodes.ARETURN);
                    mv.visitEnd();
                    /*set方法*/
                    /*TODO 将方法首字母大写，方法返回值：void*/
                    mv = cv.visitMethod(Opcodes.ACC_PUBLIC , "set" + firstCharToUpCaseForWord(nameAndTypeItem.getKey()) , "(" + nameAndTypeItem.getValue() + ")V" ,
                            null , null);
                    mv.visitCode();
                    /*TODO 方法体*/
                    mv.visitVarInsn(Opcodes.ALOAD, 0);
                    mv.visitVarInsn(Opcodes.ALOAD, 1);
                    mv.visitFieldInsn(Opcodes.PUTFIELD, sourceClassFileFullName, nameAndTypeItem.getKey(), nameAndTypeItem.getValue());
                    mv.visitMaxs(0 , 0);
                    mv.visitInsn(Opcodes.RETURN);
                    mv.visitEnd();
                });
            }
        };
        /*注意cr和cw*/
        cr.accept(cv , 0);
        byte[] getterAndSetterMethodsGen = cw.toByteArray();
        /*todo 写回类路径下，期望是覆盖而不是迭代*/
        FileUtil.writeBytes(getterAndSetterMethodsGen , new File("D:\\development\\environment\\idea\\IdeaProjects\\example\\asm\\target\\classes\\test\\pojo\\User.class"));
    }


    public static String firstCharToUpCaseForWord(String originalWord) {
        if (StringUtils.isEmpty(originalWord)) {
            return null;
        }

        char firstChar = originalWord.charAt(0);
        /**
        * 匹配65-90 97-122
         * 分析：
         * 65 - 69     6[5-9]
         * 70 - 89     [7-8][0-9]
         * 90          90
         * 97 - 99     9[7-9]
         * 100 - 119   1[0-1][0-9]
         * 120 - 122   12[0-2]
         * */
        String format = "(6[5-9])|([7-8][0-9])|(90)|(9[7-9])|(1[0-1][0-9])|(12[0-2])";
        Pattern pattern = Pattern.compile(format);
        Matcher matcher = pattern.matcher(String.valueOf((int)firstChar));
        if (!matcher.matches()) {
           throw new RuntimeException("非26英文字母组成的单词");
        }
        /**
        * 97-122   65-90
         * [A - Z] [a - z]
         * */
        int i = firstChar - 32;
        String firstCharUpCaseWord = null;

        if (i >= 65 && i <= 90) {
            firstCharUpCaseWord = originalWord.substring(0 , 1).toUpperCase().concat(originalWord.substring(1));
            System.out.println(firstCharUpCaseWord);
            return firstCharUpCaseWord;
        } else {
            System.out.println(originalWord);
            return originalWord;
        }
    }

}
